import type { AuthTokens, UserSummary } from "@/contexts/auth-context";
import { env } from "@/lib/env";

export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";

export interface FetchOptions<TBody> {
  path: string;
  method?: HttpMethod;
  body?: TBody;
  headers?: Record<string, string>;
  tokens?: AuthTokens | null;
  signal?: AbortSignal;
}

interface ApiError {
  message: string;
  statusCode?: number;
}

export async function apiFetch<TResponse, TBody = unknown>({
  path,
  method = "GET",
  body,
  headers = {},
  tokens,
  signal,
}: FetchOptions<TBody>): Promise<TResponse> {
  const url = new URL(path, env.apiBaseUrl);

  const response = await fetch(url.toString(), {
    method,
    headers: {
      "Content-Type": "application/json",
      ...(tokens?.accessToken
        ? {
            Authorization: `Bearer ${tokens.accessToken}`,
          }
        : {}),
      ...headers,
    },
    body: body ? JSON.stringify(body) : undefined,
    signal,
  });

  if (!response.ok) {
    let errorMessage = response.statusText;

    try {
      const errorJson = (await response.json()) as ApiError;
      errorMessage = errorJson.message || errorMessage;
    } catch (error) {
      // ignore json parsing error and fallback to status text
    }

    const apiError = new Error(errorMessage || "Request failed");
    (apiError as Error & { status?: number }).status = response.status;
    throw apiError;
  }

  if (response.status === 204) {
    return undefined as TResponse;
  }

  return (await response.json()) as TResponse;
}

export interface LoginResponse extends AuthTokens {
  user: UserSummary;
}

export interface LoginRequest {
  identifier: string;
  password: string;
}

export function login(request: LoginRequest) {
  return apiFetch<LoginResponse, LoginRequest>({
    path: "/auth/login",
    method: "POST",
    body: request,
  });
}

export function refreshTokens(refreshToken: string) {
  return apiFetch<AuthTokens, { refreshToken: string }>({
    path: "/auth/refresh",
    method: "POST",
    body: { refreshToken },
  });
}
